﻿using System;
using System.IO;
using io = System.IO;
using System.Net;
using Newtonsoft.Json;

namespace CometDocs
{
    internal partial class Client
    {
        /// <summary>
        /// Upload a file from the local file-system to the CometDocs store: https://www.cometdocs.com/developer/apiDocumentation#method-uploadFile
        /// </summary>
        /// <param name="token">A valid authentication token.</param>
        /// <param name="file">A file stream.</param>
        /// <param name="name">The name to give to the uploaded file.</param>
        /// <param name="folderId">The folder in which the file will be uploaded or the root folder if unspecified.</param>
        /// <returns>A FileInfo instance representing the uploaded file.</returns>
        public FileInfo UploadFile(AuthenticationToken token, Stream file, string name, long? folderId = null)
        {
            string boundary = Guid.NewGuid().ToString();
            byte[] boundaryBytes = encoding.GetBytes(boundary);

            byte[] dashDashBytes = encoding.GetBytes("--");

            byte[] CRLFBytes = encoding.GetBytes("\r\n");

            string tokenPart = "Content-Disposition: form-data; name=\"token\"\r\n" +
                               "\r\n" +
                               token.Value + "\r\n";
            byte[] tokenPartBytes = encoding.GetBytes(tokenPart);

            string folderPart = null;
            byte[] folderPartBytes = null;

            if (folderId != null)
            {
                folderPart = "Content-Disposition: form-data; name=\"folderId\"\r\n" +
                             "\r\n" +
                             folderId.ToString() + "\r\n";

                folderPartBytes = encoding.GetBytes(folderPart);
            }

            string fileHeaderPart = "Content-Disposition: form-data; name=\"file\"; filename=\"" + name + "\"\r\n" +
                                    "\r\n";
            byte[] fileHeaderPartBytes = encoding.GetBytes(fileHeaderPart);

            HttpWebRequest request = HttpWebRequest.Create(APIRoot + "uploadFile") as HttpWebRequest;
            request.Method = "POST";
            request.ContentType = "multipart/form-data; boundary=" + boundary;

            using (Stream requestStream = request.GetRequestStream())
            {
                requestStream.Write(dashDashBytes, 0, dashDashBytes.Length);
                requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
                requestStream.Write(CRLFBytes, 0, CRLFBytes.Length);
                requestStream.Write(tokenPartBytes, 0, tokenPartBytes.Length);
                requestStream.Write(dashDashBytes, 0, dashDashBytes.Length);
                requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
                requestStream.Write(CRLFBytes, 0, CRLFBytes.Length);
                if (folderId != null)
                {
                    requestStream.Write(folderPartBytes, 0, folderPartBytes.Length);
                    requestStream.Write(dashDashBytes, 0, dashDashBytes.Length);
                    requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
                    requestStream.Write(CRLFBytes, 0, CRLFBytes.Length);
                }
                requestStream.Write(fileHeaderPartBytes, 0, fileHeaderPartBytes.Length);
                file.CopyTo(requestStream);
                requestStream.Write(CRLFBytes, 0, CRLFBytes.Length);
                requestStream.Write(dashDashBytes, 0, dashDashBytes.Length);
                requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
                requestStream.Write(dashDashBytes, 0, dashDashBytes.Length);
            }

            HttpWebResponse httpResponse = request.GetResponse() as HttpWebResponse;

            string json = null;
            using (Stream responseStream = httpResponse.GetResponseStream())
            {
                using (StreamReader reader = new StreamReader(responseStream, encoding))
                {
                    json = reader.ReadToEnd();
                }
            }

            UploadFileResponse response = JsonConvert.DeserializeObject<UploadFileResponse>(json);

            CheckAndThrow(response);

            return response.File;
        }

        /// <summary>
        /// <see cref="UploadFile(AuthenticationToken, Stream, string, int?)"/>
        /// </summary>
        /// <param name="token"></param>
        /// <param name="file">A file path.</param>
        /// <param name="name"></param>
        /// <param name="folder"></param>
        /// <returns></returns>
        public FileInfo UploadFile(AuthenticationToken token, Stream file, string name, FolderInfo folder)
        {
            return UploadFile(token, file, name, folder.ID);
        }

        /// <summary>
        /// <see cref="UploadFile(AuthenticationToken, Stream, string, int?)"/>
        /// </summary>
        /// <param name="token"></param>
        /// <param name="file">A file path.</param>
        /// <param name="folder"></param>
        /// <returns></returns>
        public FileInfo UploadFile(AuthenticationToken token, Stream file, FolderInfo folder)
        {
            return UploadFile(token, file, null, folder.ID);
        }

        /// <summary>
        /// <see cref="UploadFile(AuthenticationToken, Stream, string, int?)"/>
        /// </summary>
        /// <param name="token"></param>
        /// <param name="file">A file path.</param>
        /// <param name="folderId"></param>
        /// <returns></returns>
        public FileInfo UploadFile(AuthenticationToken token, string file, long? folderId = null)
        {
            string name = Path.GetFileName(file);

            using (FileStream stream = io.File.OpenRead(file))
            {
                return UploadFile(token, stream, name, folderId);
            }
        }

        /// <summary>
        /// <see cref="UploadFile(AuthenticationToken, Stream, string, int?)"/>
        /// </summary>
        /// <param name="token"></param>
        /// <param name="file">A File object whose content will be uploaded.</param>
        /// <param name="folderId"></param>
        /// <returns></returns>
        public FileInfo UploadFile(AuthenticationToken token, File file, long? folderId = null)
        {
            using (MemoryStream stream = new MemoryStream(file.Content))
            {
                return UploadFile(token, stream, file.Name + "." + file.Extension, folderId);
            }
        }

        /// <summary>
        /// <see cref="UploadFile(AuthenticationToken, Stream, string, int?)"/>
        /// </summary>
        /// <param name="token"></param>
        /// <param name="file">A File object whose content will be uploaded.</param>
        /// <param name="folder"></param>
        /// <returns></returns>
        public FileInfo UploadFile(AuthenticationToken token, File file, FolderInfo folder)
        {
            return UploadFile(token, file, folder.ID);
        }
    }
}
